home *** CD-ROM | disk | FTP | other *** search
- /*
- File: MoreOSUtils.c
-
- Contains: A OS utility library.
-
- Written by: Quinn
-
- Copyright: Copyright © 1998 by Apple Computer, Inc., all rights reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- Change History (most recent first):
-
- <2> 16/3/99 Quinn Rolled in InterfaceDisableLib from DTS Technote 1137.
- <1> 1/3/99 Quinn First checked in.
- */
-
- /////////////////////////////////////////////////////////////////////
-
- // MoreIsBetter Setup
-
- #include "MoreSetup.h"
-
- // Mac OS interfaces
-
- #include <Traps.h>
- #include <Gestalt.h>
- #include <CodeFragments.h>
- #include <MixedMode.h>
-
- // MIB Prototypes
-
- #include "MoreInterfaceLib.h"
-
- // Our Prototypes
-
- #include "MoreOSUtils.h"
-
- /////////////////////////////////////////////////////////////////////
-
- // We require Universal Interfaces 3.2 because earlier versions
- // either mess up the prototype for FlushCodeCacheRange, or don't
- // define it for CFM code, or both!
-
- #if UNIVERSAL_INTERFACES_VERSION < 0x0320
- #error MoreOSUtils.c requires Universal Interfaces 3.2 or higher.
- #endif
-
- /////////////////////////////////////////////////////////////////////
- #pragma mark ----- 68K Code Cache Flush -----
-
- // Define the _vCacheFlush trap (used by FlushCodeCache), which is not included
- // in Universal Interfaces.
-
- enum {
- _vCacheFlush = 0xA0BD
- };
-
- #if TARGET_CPU_68K
-
- // The following routines are used to flush the cache using direct
- // 680x0 instructions. They are only used if no OS support is available.
- //
- // Because these routines take no parameters and return no results,
- // they are equally effective for both classic 68K and CFM-68K.
-
- static void FlushCacheViaCACR(void) =
- // FlushCacheViaCACR is an inline assembly routine that flushes both the
- // instruction and data caches by writing directly to the CACR. Used
- // as a last resort by MakeData68KExecutable on 68020 and 68030.
- {
- 0x4E7A, 0x0002, // MOVEC CACR,D0
- 0x08C0, 0x0003, // BSET #3,D0
- 0x4E7B, 0x0002 // MOVEC D0,CACR
- };
-
- static void FlushCacheWithCPushA(void) =
- // FlushCacheWithCPushA is another inline assembly routine that flushes caches
- // on the MC68040 using the CPushA instruction. Used as a last resort
- // by MakeData68KExecutable on 68040.
- {
- 0x4E71, // NOP ; to clear pending writes
- 0xF4F8 // CPUSHA BC
- };
-
- #endif
-
- extern pascal OSStatus MakeData68KExecutable(void *address, ByteCount count)
- // See comment in interface part.
- {
- OSErr err;
-
- #if TARGET_CPU_PPC
- MoreAssertQ(GetOSTrapAddress(_HWPriv) != GetToolTrapAddress(_Unimplemented));
- #endif
-
- // Step 1. If we have _HWPriv, try calling FlushCodeCacheRange. If that
- // returns an error, call FlushCodeCache. Two important assumptions:
- //
- // a) any machine that has FlushCodeCacheRange implemented will necessarily
- // implement FlushCodeCache.
- // b) PowerPC computers always have FlushCodeCacheRange implemented, so
- // we don't need Mixed Mode glue for FlushCodeCache because we'll never
- // need it on a PowerPC.
-
- if ( GetOSTrapAddress(_HWPriv) != GetToolTrapAddress(_Unimplemented) ) {
- err = MoreFlushCodeCacheRange(address, count);
- #if TARGET_CPU_68K
- if (err != noErr) {
- MoreAssertQ(GetOSTrapAddress(_vCacheFlush) != GetToolTrapAddress(_Unimplemented));
- FlushCodeCache();
- }
- #else
- MoreAssertQ(err == noErr);
- #endif
-
- // Step 2. If we don't have _HWPriv, look to see whether _vCacheFlush
- // (ie FlushCodeCache) is implemented. If it is, we'll just call it.
-
- } else if ( GetOSTrapAddress(_vCacheFlush) != GetToolTrapAddress(_Unimplemented) ) {
-
- // The call to FlushCodeCache is conditionalised because
- // Universal Interfaces does not export the call unless
- // you're generating 68K code. *sigh* But that's OK because
- // all PowerPC machines have _HWPriv implemented, so this
- // code won't run.
-
- #if TARGET_CPU_68K
- FlushCodeCache();
- #else
- MoreAssertQ(false);
- #endif
-
- // Step 3. Finally, if neither of these traps is implemented, we're just
- // going to execute 680x0 instructions directly. Note that we have to
- // execute different instructions based on the 680x0 variant.
-
- } else {
-
- // Obviously, this is only going to work (or indeed compile) if we're
- // generating 68K code. That's cool, because if we're generating PowerPC
- // code, we must end up running on a PowerPC, and that always has
- // FlushCodeCacheRange so we never get here.
-
- #if TARGET_CPU_68K
- {
- UInt32 gestaltResponse;
-
- if (Gestalt(gestaltProcessorType, (SInt32 *) &gestaltResponse) == noErr) {
- if (gestaltResponse >= gestalt68020) {
- if (gestaltResponse <= gestalt68030) {
- FlushCacheViaCACR();
- } else {
- FlushCacheWithCPushA();
- }
- }
- }
- }
- #else
- MoreAssertQ(false);
- #endif
- }
-
- // Basically this routine can't fail or, more accurately,
- // there are no expected failure cases. So we always return
- // noErr. In fact, the only reason this routine is defined to
- // return an error code is for symmetry with MakeDataPowerPCExecutable,
- // which has to return an error code in the classic 68K case.
-
- return noErr;
- }
-
- /////////////////////////////////////////////////////////////////////
- #pragma mark ----- PowerPC Code Cache Flush -----
-
- #if TARGET_CPU_68K && !TARGET_RT_MAC_CFM
-
- // This chunk of code implements the classic 68K case for MakeDataPowerPCExecutable,
- // ie we're running classic 68K code that's generating PowerPC instructions. Boy,
- // is this a pain to implement. We have to connect up to Interface, find the
- // appropriate symbol, build a routine descriptor for it, and then call it.
-
- enum {
- uppMakeDataExecutableProcInfo = kPascalStackBased |
- STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(void *))) |
- STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(unsigned long)))
- };
-
- static CFragConnectionID gInterfaceLib = nil;
- static UniversalProcPtr gMakeDataExecutable = nil;
-
- typedef pascal void (*MakeDataExecutableProcPtr)(void *address, unsigned long count);
-
- static pascal OSStatus MakeDataPowerPCExecutableFromClassic(void *address, ByteCount count)
- {
- OSErr err;
- Ptr junkMain;
- Str255 junkMessage;
- CFragSymbolClass junkClass;
- ProcPtr routinePtr;
-
- MoreAssertQ(GetToolTrapAddress(_CodeFragmentDispatch) != GetToolTrapAddress(_Unimplemented));
- MoreAssertQ(GetToolTrapAddress(_MixedModeDispatch) != GetToolTrapAddress(_Unimplemented));
-
- err = noErr;
- if (gMakeDataExecutable == nil) {
-
- // We're not connected to InterfaceLib let, let's connect, find the MakeDataExecutable
- // symbol, and build a routine descriptor for it.
-
- err = GetSharedLibrary("\pInterfaceLib", kPowerPCCFragArch, kLoadCFrag, &gInterfaceLib, &junkMain, junkMessage);
- if (err == noErr) {
- err = FindSymbol(gInterfaceLib, "\pMakeDataExecutable", (Ptr *) &routinePtr, &junkClass);
- }
- if (err == noErr) {
- MoreAssertQ(junkClass == kTVectorCFragSymbol);
- gMakeDataExecutable = NewRoutineDescriptorTrap(routinePtr, uppMakeDataExecutableProcInfo, kPowerPCISA);
- if (gMakeDataExecutable == nil) {
- err = memFullErr;
- }
- }
-
- // If any of this failed, let's shut it all down gracefully.
-
- if (err != noErr) {
- TermMoreOSUtils();
- }
- }
-
- // If we have a UPP for MakeDataExecutable, call it.
-
- if (gMakeDataExecutable != nil) {
- ((MakeDataExecutableProcPtr) gMakeDataExecutable)(address, count);
- }
- return err;
- }
-
- extern pascal void TermMoreOSUtils(void)
- // See comment in interface part.
- {
- OSErr junk;
-
- if ( gMakeDataExecutable != nil ) {
- DisposeRoutineDescriptorTrap(gMakeDataExecutable);
- gMakeDataExecutable = nil;
- }
- if ( gInterfaceLib != nil ) {
- junk = CloseConnection(&gInterfaceLib);
- MoreAssertQ(junk == noErr);
- gInterfaceLib = nil;
- }
- }
-
- #endif
-
- extern pascal OSStatus MakeDataPowerPCExecutable(void *address, ByteCount count)
- // See comment in interface part.
- {
- // Note: We don't have to worry about the CFM-68K case because
- // CFM-68K won't run on PowerPC machines, so there's no sense
- // in trying to make some data PowerPC-executable from CFM-68K code.
-
- #if TARGET_RT_MAC_CFM
- #if TARGET_CPU_68K
- #pragma unused(address)
- #pragma unused(count)
- MoreAssertQ(false);
- return noErr;
- #else
- MakeDataExecutable(address, count);
- return noErr;
- #endif
- #else
- return MakeDataPowerPCExecutableFromClassic(address, count);
- #endif
- }
-
- /////////////////////////////////////////////////////////////////////
- #pragma mark ----- Interrupt Enable and Disable -----
-
- #if GENERATINGPOWERPC
-
- // PowerPC Specific Code
-
- // On PPC, we use MixedMode to handle moving the PPC parameters
- // into the right 68K registers and back again. This make our
- // 68K very easy to write.
-
- enum {
- kGetSRProcInfo = kRegisterBased
- | RESULT_SIZE(SIZE_CODE(sizeof(UInt16)))
- | REGISTER_RESULT_LOCATION(kRegisterD0),
- kSetSRProcInfo = kRegisterBased
- | RESULT_SIZE(0)
- | REGISTER_ROUTINE_PARAMETER(1, kRegisterD0, SIZE_CODE(sizeof(UInt16)))
- };
-
- // We define the 68K as a statically initialised data structure.
- // The use of MixedMode to call these routines makes the routines
- // themselves very simple.
-
- static UInt16 gGetSR[] = {
- 0x40c0, // move sr,d0
- 0x4e75 // rts
- };
-
- static UInt16 gSetSR[] = {
- 0x46c0, // move d0,sr
- 0x4e75 // rts
- };
-
- static UInt16 GetSR(void)
- // Returns the current value of the SR, interrupt mask
- // and all! This routine uses MixedMode to call the gGetSR data
- // structure as if it was 68K code (which it is!).
-
- {
- return CallUniversalProc( (UniversalProcPtr) &gGetSR, kGetSRProcInfo);
- }
-
- static void SetSR(UInt16 newSR)
- // Returns the value of the SR, including the interrupt mask and all
- // the flag bits. This routine uses MixedMode to call the gGetSR data
- // structure as if it was 68K code (which it is!).
- {
- CallUniversalProc( (UniversalProcPtr) &gSetSR, kSetSRProcInfo, newSR);
- }
-
- #else
-
- // Classic 68K and CFM-68K Specific Code
-
- // On classic 68K (and CFM-68K) we can simply access the
- // 68K SR register using some inline procedures.
-
- static UInt16 GetSR(void) = {
- 0x40c0 // move sr,d0
- };
-
- #pragma parameter SetSR(__D0)
- static void SetSR(UInt16 newSR) = {
- 0x46c0 // move d0,sr
- };
-
- #endif
-
- extern pascal UInt16 GetInterruptMask(void)
- // See comment in header file.
- {
- return (GetSR() >> 8) & 7;
- }
-
- extern pascal UInt16 SetInterruptMask(UInt16 newMask)
- // See comment in header file.
- {
- UInt16 currentSR;
-
- MoreAssertQ(newMask >= 0 && newMask <= 7);
-
- currentSR = GetSR();
- SetSR( (currentSR & 0xF8FF) | (newMask << 8) );
-
- return (currentSR >> 8) & 7;
- }
-